import os
import sys
import inspect
import logging
import datetime

class Logger:
    CRITICAL = 50
    FATAL = CRITICAL
    ERROR = 40
    WARNING = 30
    WARN = WARNING
    INFO = 20
    DEBUG = 10
    NOTSET = 0

    def __init__(self, log_dir=os.path.join(os.path.dirname(__file__), 'logs')):
        self.log_dir = log_dir

    def log(self, level, msg, exception=False):

        date_ = datetime.datetime.now().date()
        self.logger = logging.getLogger(str(date_))

        formatter = logging.Formatter('' + \
            '%(asctime)s[%(levelname)s]:' + \
            '%(processName)s[%(process)d]:' + \
            '%(threadName)s[%(thread)d]%(message)s')

        if hasattr(sys, 'gettrace') and sys.gettrace() is not None:
            stream_handler = logging.StreamHandler()
            stream_handler.setFormatter(formatter)
            if len(self.logger.handlers) == 0:
                self.logger.addHandler(stream_handler)
            self.logger.setLevel(logging.DEBUG)
        else:
            log_path = os.path.join(self.log_dir, f'{date_}.log')
            os.makedirs(os.path.dirname(log_path), exist_ok=True)
            file_handler = logging.FileHandler(log_path, encoding='utf-8')
            file_handler.setFormatter(formatter)
            if len(self.logger.handlers) == 0:
                self.logger.addHandler(file_handler)
            self.logger.setLevel(logging.INFO)

        if exception:
            tb = sys.exc_info()[2]
            if hasattr(tb, 'tb_next') and tb.tb_next is not None:
                tb = tb.tb_next
            if hasattr(tb, 'tb_next') and tb.tb_next is not None and 'lambda' in tb.tb_frame.f_code.co_name:
                tb = tb.tb_next
            funcName = tb.tb_frame.f_code.co_name
            module = os.path.basename(tb.tb_frame.f_code.co_filename)
            lineno = tb.tb_lineno
        else:
            funcName = inspect.currentframe().f_back.f_code.co_name
            module = os.path.basename(inspect.currentframe().f_back.f_code.co_filename)
            lineno = inspect.currentframe().f_back.f_lineno

        self.logger.log(level, f':{funcName}[{module}:{lineno}]\n{msg}')

def __test():
    logger = Logger()
    logger.log(logger.DEBUG, 'DEBUG Message')
    logger.log(logger.INFO, 'INFO Message')
    logger.log(logger.WARN, 'WARN Message')
    logger.log(logger.ERROR, 'ERROR Message')
    logger.log(logger.FATAL, 'FATAL Message')

    try:
        1/0
    except Exception as e:
        logger.log(logger.FATAL, f'FATAL Message\n{e}', exception=True)

if __name__ == '__main__':
    __test()